package com.kexio.auth.service;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.kexio.auth.annotation.RateLimit;
import com.kexio.auth.context.TenantContextHolder;
import com.kexio.auth.dto.UserAuthInfo;
import com.kexio.auth.util.AuthContextUtils;

import jakarta.servlet.http.HttpServletRequest;

/**
 * 限流服务
 * 
 * 基于Redis实现的分布式限流服务，支持多种限流算法和策略：
 * - 滑动窗口算法（精确控制）
 * - 令牌桶算法（允许突发）
 * - 固定窗口算法（简单高效）
 * 
 * 支持多种限流维度：
 * - 用户级限流
 * - IP级限流
 * - 全局限流
 * - 自定义key限流
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Service
public class RateLimitService {
    
    private static final Logger logger = LoggerFactory.getLogger(RateLimitService.class);
    
    @Autowired(required = false)
    private StringRedisTemplate redisTemplate;
    
    private static final String RATE_LIMIT_KEY_PREFIX = "rate_limit:";
    private static final String SLIDING_WINDOW_SCRIPT = """
        local key = KEYS[1]
        local window = tonumber(ARGV[1])
        local limit = tonumber(ARGV[2])
        local current_time = tonumber(ARGV[3])
        
        -- 清除过期的记录
        redis.call('zremrangebyscore', key, 0, current_time - window)
        
        -- 获取当前窗口内的请求数
        local current_requests = redis.call('zcard', key)
        
        if current_requests < limit then
            -- 添加当前请求
            redis.call('zadd', key, current_time, current_time)
            -- 设置过期时间
            redis.call('expire', key, math.ceil(window / 1000))
            return {0, limit - current_requests - 1}
        else
            return {1, 0}
        end
    """;
    
    private static final String TOKEN_BUCKET_SCRIPT = """
        local key = KEYS[1]
        local capacity = tonumber(ARGV[1])
        local tokens = tonumber(ARGV[2])
        local interval = tonumber(ARGV[3])
        local current_time = tonumber(ARGV[4])
        
        local bucket_info = redis.call('hmget', key, 'tokens', 'last_refill_time')
        local current_tokens = tonumber(bucket_info[1]) or capacity
        local last_refill_time = tonumber(bucket_info[2]) or current_time
        
        -- 计算需要添加的令牌数
        local time_passed = current_time - last_refill_time
        local tokens_to_add = math.floor(time_passed / interval * tokens)
        current_tokens = math.min(capacity, current_tokens + tokens_to_add)
        
        if current_tokens >= 1 then
            current_tokens = current_tokens - 1
            redis.call('hmset', key, 'tokens', current_tokens, 'last_refill_time', current_time)
            redis.call('expire', key, math.ceil(interval / 1000 * capacity))
            return {0, current_tokens}
        else
            redis.call('hmset', key, 'tokens', current_tokens, 'last_refill_time', current_time)
            redis.call('expire', key, math.ceil(interval / 1000 * capacity))
            return {1, 0}
        end
    """;
    
    private static final String FIXED_WINDOW_SCRIPT = """
        local key = KEYS[1]
        local window = tonumber(ARGV[1])
        local limit = tonumber(ARGV[2])
        local current_time = tonumber(ARGV[3])
        
        -- 计算当前窗口
        local window_start = math.floor(current_time / window) * window
        local window_key = key .. ':' .. window_start
        
        -- 获取当前窗口的请求数
        local current_requests = redis.call('incr', window_key)
        redis.call('expire', window_key, math.ceil(window / 1000))
        
        if current_requests <= limit then
            return {0, limit - current_requests}
        else
            return {1, 0}
        end
    """;
    
    /**
     * 检查是否被限流
     * 
     * @param rateLimit 限流注解
     * @param request HTTP请求对象
     * @param methodSignature 方法签名
     * @return 是否被限流
     */
    public boolean isRateLimited(RateLimit rateLimit, HttpServletRequest request, String methodSignature) {
        if (!rateLimit.enabled()) {
            logger.debug("限流功能已禁用: {}", methodSignature);
            return false;
        }
        
        if (redisTemplate == null) {
            logger.warn("Redis未配置，限流功能不可用: {}", methodSignature);
            return false;
        }
        
        try {
            // 1. 构建限流key
            String rateLimitKey = buildRateLimitKey(rateLimit, request, methodSignature);
            
            // 2. 执行限流检查
            boolean limited = executeRateLimitCheck(rateLimitKey, rateLimit);
            
            if (limited) {
                logger.warn("请求被限流: {} - key: {} - 限制: {}次/{}{}", 
                    methodSignature, rateLimitKey, rateLimit.count(), rateLimit.time(), 
                    rateLimit.timeUnit().toString().toLowerCase());
            } else {
                logger.debug("限流检查通过: {} - key: {}", methodSignature, rateLimitKey);
            }
            
            return limited;
            
        } catch (Exception e) {
            logger.error("限流检查异常: {} - {}", methodSignature, e.getMessage(), e);
            // 异常时默认不限流，避免影响业务
            return false;
        }
    }
    
    /**
     * 构建限流key
     */
    private String buildRateLimitKey(RateLimit rateLimit, HttpServletRequest request, String methodSignature) {
        StringBuilder keyBuilder = new StringBuilder(RATE_LIMIT_KEY_PREFIX);
        
        // 添加租户信息
        String tenantId = TenantContextHolder.getCurrentTenantId();
        if (StringUtils.hasText(tenantId)) {
            keyBuilder.append("tenant:").append(tenantId).append(":");
        }
        
        // 根据限流类型构建key
        switch (rateLimit.limitType()) {
            case USER:
                UserAuthInfo user = AuthContextUtils.getCurrentUser();
                if (user != null) {
                    keyBuilder.append("user:").append(user.getUserId());
                } else {
                    // 未登录用户按IP限流
                    keyBuilder.append("ip:").append(getClientIpAddress(request));
                }
                break;
                
            case IP:
                keyBuilder.append("ip:").append(getClientIpAddress(request));
                break;
                
            case GLOBAL:
                keyBuilder.append("global");
                break;
                
            case CUSTOM:
                if (StringUtils.hasText(rateLimit.key())) {
                    keyBuilder.append("custom:").append(rateLimit.key());
                } else {
                    // 自定义key为空时fallback到方法名
                    keyBuilder.append("method:").append(methodSignature);
                }
                break;
        }
        
        // 添加方法签名和算法标识
        keyBuilder.append(":").append(methodSignature.hashCode())
                 .append(":").append(rateLimit.algorithm().name().toLowerCase());
        
        return keyBuilder.toString();
    }
    
    /**
     * 执行限流检查
     */
    private boolean executeRateLimitCheck(String key, RateLimit rateLimit) {
        long currentTime = System.currentTimeMillis();
        long windowSize = rateLimit.timeUnit().toMillis(rateLimit.time());
        
        switch (rateLimit.algorithm()) {
            case SLIDING_WINDOW:
                return executeSlidingWindowCheck(key, windowSize, rateLimit.count(), currentTime);
                
            case TOKEN_BUCKET:
                return executeTokenBucketCheck(key, rateLimit.count(), windowSize, currentTime);
                
            case FIXED_WINDOW:
                return executeFixedWindowCheck(key, windowSize, rateLimit.count(), currentTime);
                
            default:
                logger.warn("未知的限流算法: {}", rateLimit.algorithm());
                return false;
        }
    }
    
    /**
     * 滑动窗口算法检查
     */
    @SuppressWarnings("unchecked")
    private boolean executeSlidingWindowCheck(String key, long window, int limit, long currentTime) {
        RedisScript<java.util.List> script = RedisScript.of(SLIDING_WINDOW_SCRIPT, java.util.List.class);
        java.util.List<Long> result = (java.util.List<Long>) redisTemplate.execute(script, 
            Arrays.asList(key), String.valueOf(window), String.valueOf(limit), String.valueOf(currentTime));
        
        return result != null && result.size() > 0 && result.get(0) == 1;
    }
    
    /**
     * 令牌桶算法检查
     */
    @SuppressWarnings("unchecked")
    private boolean executeTokenBucketCheck(String key, int capacity, long refillInterval, long currentTime) {
        RedisScript<java.util.List> script = RedisScript.of(TOKEN_BUCKET_SCRIPT, java.util.List.class);
        java.util.List<Long> result = (java.util.List<Long>) redisTemplate.execute(script, 
            Arrays.asList(key), String.valueOf(capacity), String.valueOf(capacity), String.valueOf(refillInterval), String.valueOf(currentTime));
        
        return result != null && result.size() > 0 && result.get(0) == 1;
    }
    
    /**
     * 固定窗口算法检查
     */
    @SuppressWarnings("unchecked")
    private boolean executeFixedWindowCheck(String key, long window, int limit, long currentTime) {
        RedisScript<java.util.List> script = RedisScript.of(FIXED_WINDOW_SCRIPT, java.util.List.class);
        java.util.List<Long> result = (java.util.List<Long>) redisTemplate.execute(script, 
            Arrays.asList(key), String.valueOf(window), String.valueOf(limit), String.valueOf(currentTime));
        
        return result != null && result.size() > 0 && result.get(0) == 1;
    }
    
    /**
     * 获取客户端真实IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (StringUtils.hasText(xForwardedFor) && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (StringUtils.hasText(xRealIp) && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
    
    /**
     * 获取限流统计信息
     * 
     * @param key 限流key
     * @return 统计信息
     * @deprecated 未实现，预留扩展
     */
    @Deprecated
    public RateLimitStats getRateLimitStats(String key) {
        logger.warn("getRateLimitStats未实现，返回空统计信息");
        return new RateLimitStats();
    }
    
    /**
     * 限流统计信息
     */
    public static class RateLimitStats {
        private int currentRequests = 0;
        private int remainingQuota = 0;
        private long resetTime = 0;
        
        // getters and setters
        public int getCurrentRequests() { return currentRequests; }
        public void setCurrentRequests(int currentRequests) { this.currentRequests = currentRequests; }
        
        public int getRemainingQuota() { return remainingQuota; }
        public void setRemainingQuota(int remainingQuota) { this.remainingQuota = remainingQuota; }
        
        public long getResetTime() { return resetTime; }
        public void setResetTime(long resetTime) { this.resetTime = resetTime; }
    }
}
